home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / tvcopy.exe / PICKCOPY.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1993-04-18  |  23.3 KB  |  749 lines

  1. {$X+}
  2. program PickCopy; 
  3.  
  4. {This program provides an example of how to pop out of a dialog box, open
  5. a list box containing data from a file, then copy the desired data into
  6. several input lines in the input dialog box.  The code to do this was
  7. written by Steve Schafer in response to a question by me.
  8.  
  9. I have added code that reads, displays, adds to, edits, removes or prints
  10. data in collections, stored in ASCII files.  This is modified from a demo
  11. program called Phone.pas available either on CIS or from Borland's BBS.
  12.  
  13. I was unhappy with having the data stored in an object file since it would
  14. thus be unusable without this program and thus not be amenable to revision
  15. with a simple text editor.  Having the data stored in an ASCII file rather
  16. with a stream file violates the principles of encapsulating data and code,
  17. but I love it!.
  18.  
  19. Be aware that this example shows how Steven Schafer would do the pick and
  20. copy, but he says that there are certainly other ways which would work
  21. just as well.
  22.  
  23. If you have comments or improvements, please send them along to me:
  24.  
  25. Stewart Midwinter 74670,1306.
  26.  
  27. Cheers, SAM  }
  28.  
  29. uses
  30.   Memwatch,                    {warns of unreleased heap, by J.J. Stein}
  31.                                {available on CIS Pascal Library 1      }
  32.   Drivers,Objects,Views,Menus, {Turbo Vision units                     }
  33.   Dialogs,StdDlg,MsgBox,App,   {Turbo Vision units                     }
  34.   Gadgets,                     {Turbo Vision units                     }
  35.   Dos,Crt,                     {standard Turbo Pascal units            }
  36.   SList;                       {handles editing collections            }
  37.  
  38.  
  39. const
  40.   cmNew             = 101;  { Initialise a new site file                   }
  41.   cmOpen            = 102;  { Open an existing site file, read into memory }
  42.   cmNewDialog       = 103;  { create Details-type dialog                   }
  43.   cmSiteList        = 201;  { button to open list box to pick a site       }
  44.   cmListDlg         = 107;  { command to open list box dialog              }
  45.  
  46.  { NumSites is the number of sites listed in the "Flight Details" dialog.  }
  47.  
  48.   NumSites          = 2;
  49.  
  50. type
  51.   PsiteApp = ^TsiteApp;
  52.   TsiteApp = object (TApplication)
  53.     CurrentFile: PathStr; 
  54.     HeapViewer: PHeapView;
  55.     constructor Init;
  56.     procedure NewsiteList;
  57.     procedure OpensiteList;
  58.     procedure SavesiteList;
  59.     procedure HandleEvent (var Event: TEvent); virtual;
  60.     procedure InitMenuBar; virtual;
  61.     procedure InitStatusLine; virtual;
  62.     procedure Idle; virtual;
  63.     destructor Done; virtual;
  64.     end;
  65.  
  66.   String80 = String[80];
  67.   String60 = String[60];
  68.   String40 = String[40];
  69.   String14 = String[14];
  70.  
  71.   PSiteRec = ^TSiteRec;
  72.   TSiteRec = record{object(TObject)}
  73.     FNum: Word;
  74.     FName: string40;
  75.     FLat, FLong: String14;
  76.     FInfo: String80;
  77.   end;
  78.  
  79.  { TSite is an object type designed to hold all of the information for a
  80.    site. It is a descendant of TObject so that we can store it in a
  81.    collection. }
  82.  
  83.   PLSite = ^TLSite;
  84.   TLSite = object(TObject)
  85.     LName, LLat, LLong,LInfo: PString;
  86.     constructor Init( AName: String40;
  87.                       ALat,ALong: String14;
  88.                       AInfo: String80);
  89.     destructor Done; virtual;
  90.   end;
  91.  
  92.  { TSiteCollection is a simple descendant of TSortedCollection, which
  93.    assumes that the objects contained in it are all of type TSite. The only
  94.    change is the new Compare method, which sorts the collection on the Name
  95.    field of the TSites. }
  96.  
  97.   PLSiteCollection = ^TLSiteCollection;     {contains a TLSite object}
  98.   TLSiteCollection = object(TSortedCollection)
  99.     function Compare (Key1,Key2: pointer): integer; virtual;
  100.     procedure FreeItem(Item: pointer); virtual;
  101.   end;
  102.  
  103.  { TSiteListBox is a list box which holds TSites. The GetText method knows
  104.    that the items in the list box collection are TSites, so it extracts the
  105.    Name field for display in the list box. }
  106.  
  107.   PLSiteListBox = ^TLSiteListBox;
  108.   TLSiteListBox = Object(TListBox)
  109.     function GetText (item: integer; MaxLen: integer): string; virtual;
  110.     procedure HandleEvent(var Event: TEvent); virtual;
  111.   end;
  112.  
  113.   PListDialog = ^TListDialog;
  114.   TListDialog = object(TDialog)
  115.     SitePicklist: PLSiteListBox;
  116.     SiteType: PRadioButtons;
  117.     constructor Init;
  118.   end;
  119.  
  120.  { TSiteDialog is the "Flight Details" dialog box. Note that I've added
  121.    fields corresponding to all of the input lines; this is so that they are
  122.    directly accessible from HandleEvent. }
  123.  
  124.   PSiteDialog = ^TSiteDialog;
  125.   TSiteDialog = object(TDialog)
  126.     SSiteName,SSiteLat,SSiteLong: array[0..NumSites-1] of PInputline;
  127.     constructor Init;
  128.     procedure HandleEvent(var Event:TEvent); virtual;
  129.   end;
  130.  
  131.   SCoordData          = record
  132.     SiteName:        string80;
  133.     SiteLat:         String14;
  134.     SiteLong:        String14;
  135.   end;
  136.  
  137.   DialogPtr = ^SDialogData;
  138.   SDialogData       = record      {data record for inputting coordinates}
  139.     PlaceData:       array[0..1] of SCoordData;
  140.   end;
  141.  
  142.   NamesArray = array[0..1] of string;
  143.  
  144. const
  145.   SiteDialogData:
  146.     SDialogData = (PlaceData: (
  147.                               (SiteName: ''; SiteLat: '00'; SiteLong: '000'),
  148.                               (SiteName: ''; SiteLat: '00'; SiteLong: '000')
  149.                               ));
  150.   ChosenLocn: NamesArray = ( 'Start point','Finish point');
  151.  
  152. var
  153.   siteApp: TsiteApp;          {place here or CurrentFile will not be visible}
  154.  
  155. var
  156.   TheLSiteCollection: PLSiteCollection;
  157.  
  158.  
  159. { TsiteApp methods }
  160.  
  161. constructor TsiteApp.Init;
  162. var R: TRect;
  163. begin
  164.   TApplication.Init;
  165.   RegisterObjects;
  166.   RegisterViews;
  167.   RegisterMenus;
  168.   RegisterDialogs;
  169.   RegisterApp;
  170.   Registersite;
  171.   GetExtent(R);
  172.   Dec(R.B.X);
  173.   R.A.X := R.B.X - 9; R.A.Y := R.B.Y - 1;
  174.   HeapViewer := New(PHeapView,Init(R));
  175.   Insert(HeapViewer);
  176.   CurrentFile := '';
  177.   Messagebox( #3'Test of Data Collection &'#13+
  178.               #3'Input Dialog w/Pick List',nil,mfinformation+mfOkButton);
  179. end;
  180.  
  181. procedure TSiteApp.Idle;    {■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■}
  182. begin
  183.   TApplication.Idle;
  184.   HeapViewer^.Update;
  185. end;
  186.  
  187.  
  188.  {----------------------------- TLSite -----------------------------------}
  189.  
  190.  { TLSite.Init just takes the string values passed to it and inserts them
  191.    into the fields. Note that any blank strings are replaced with a single
  192.    space. I've done this because calling NewStr ('') returns a NIL pointer,
  193.    which can cause problems in protected mode. }
  194.  
  195. constructor TLSite.Init (AName: String40; ALat,ALong: String14; AInfo: String80);
  196. begin
  197.  TObject.Init;
  198.  if AName <> '' then LName := NewStr (AName) else LName := NewStr (' ');
  199.  if ALat  <> '' then LLat  := NewStr (ALat)  else LLat  := NewStr (' ');
  200.  if ALong <> '' then LLong := NewStr (ALong) else LLong := NewStr (' ');
  201.  if AInfo <> '' then LInfo := NewStr (AInfo) else LInfo := NewStr (' ');
  202.  end;
  203.  
  204.  { TSite.Done simply releases the memory allocated in TSite.Init. }
  205.  
  206. destructor TLSite.Done;
  207. begin
  208.  DisposeStr (LName);
  209.  DisposeStr (LLat);
  210.  DisposeStr (LLong);
  211.  DisposeStr (LInfo);
  212.  TObject.Done;
  213. end;
  214.   {------------------------------ end of TLSite ---------------------------}
  215.  
  216.   {---------------------------- TLSiteCollection --------------------------}
  217.  
  218.  { TSiteCollection.Compare extracts the Name fields from the two items and
  219.    compares them alphabetically. }
  220.  
  221. function TLSiteCollection.Compare (Key1,Key2: Pointer): Integer;
  222. begin
  223.   if PLSite (Key1)^.LName^ < PLSite (Key2)^.LName^ then Compare := - 1
  224.   else if PLSite (Key1)^.LName^ > PLSite (Key2)^.LName^ then Compare := 1
  225.   else Compare := 0;
  226. end;
  227.  
  228. procedure TLSiteCollection.FreeItem;
  229. begin
  230.   if TheLSiteCollection <> nil then
  231.   begin
  232.   DisposeStr(PLSite(Item)^.LName);
  233.   DisposeStr(PLSite(Item)^.LLat);
  234.   DisposeStr(PLSite(Item)^.LLong);
  235.   DisposeStr(PLSite(Item)^.LInfo);
  236.   Dispose(PLSite(Item))
  237.   end;
  238. end;
  239.   {------------------------- end of TLSiteCollection ---------------------}
  240.  
  241.  
  242. { The FileExists function checks to see if the filename passed to it }
  243. { refers to an existing file.                                        }
  244.  
  245. function FileExists (FileName: PathStr): Boolean;
  246. var
  247.   F: File;
  248. begin
  249. Assign (F,FileName);
  250. {$I-}
  251. Reset (F);
  252. {$I+}
  253. if IOResult <> 0 then FileExists := False
  254. else begin
  255.   FileExists := True;
  256.   Close (F);
  257.   end;
  258. end;
  259.  
  260.   {--------------------------- SiteColl methods ---------------------------}
  261.  
  262. { NewsiteList instantiates a new Tsitecoll object (empty), and invokes a
  263.   dialog box via siteList^.Show, so that the user can enter information
  264.   into the Tsitecoll. At the time the dialog is closed, the value returned
  265.   by Show is checked. If the dialog was not closed with a cmCancel, the
  266.   Tsitecoll is saved to disk.                                              }
  267.  
  268. procedure TsiteApp.NewsiteList;
  269. begin
  270. siteList := New (Psitecoll,Init (10,5));
  271. SiteList^.Duplicates:= true;
  272. SiteList^.Count:= 0;
  273. CurrentFile := '';
  274. if siteList^.Show <> cmCancel then SavesiteList else
  275.    begin
  276.      SiteList^.FreeAll;
  277.      Dispose(ViewDialog,Done);
  278.      Dispose(SiteList,Done);
  279.    end;
  280. end;
  281.  
  282. { OpensiteList is similar to NewsiteList, except that the Tsitecoll is
  283.   loaded from an existing disk file. Error handling is rudimentary.       }
  284.  
  285. procedure Detrail(var T: String14); forward;
  286.  
  287. procedure TsiteApp.OpensiteList;
  288. var
  289.   LineOfFile:   Pword;
  290.   D: PFileDialog;
  291.   S: String;
  292.   P: PSite;
  293.   F: text;
  294.   FileSite: TSiteRec;
  295. begin
  296. D := New (PFileDialog,Init ('*.DAT','Open site file','~N~ame',
  297.                             fdOKButton + fdHelpButton,100));
  298. if Desktop^.ExecView (D) <> cmCancel then
  299.   begin                                    {Open a file}
  300.   D^.GetFileName (CurrentFile);
  301.   if FileExists (CurrentFile) then         {open existing site file}
  302.   begin
  303.   SiteList := New (Psitecoll,Init (10,5));
  304.               {have to init each time if reading from an ASCII file}
  305.               {read items from file, insert into collection        }
  306.   SiteList^.Duplicates:= true;
  307.   SiteList^.Count:= 0;
  308.   Assign(F,CurrentFile);
  309.   Reset(F);
  310.   New(LineofFile);
  311.   LineOfFile^ := 0;
  312.   while not Eof(F) do
  313.     begin
  314.     Readln(F, S);
  315.     Inc(LineOfFile^);
  316.     if length(S) > 0 then
  317.       begin
  318.       with FileSite do
  319.       begin
  320.          FNum := LineofFile^;
  321.          FName := copy(S, 1, (pos('/', S) - 1));
  322.          if pos('/', S) <> 0 then System.Delete(S, 1, pos('/', S));
  323.          FLat  := copy(S, 1, (pos('/', S) - 1));
  324.          if pos('/', S) <> 0 then System.Delete(S, 1, pos('/', S));
  325.          Detrail(FLat);
  326.          FLong := S;
  327.          Detrail(FLong);
  328.          Readln(F,S);
  329.          FInfo := S;
  330.          P := New (PSite,Init (FName,FLat,FLong,FInfo));
  331.          SiteList^.Insert (P)
  332.        end
  333.        end
  334.     end; {end While not EOF}
  335.   Close(F);
  336.   Dispose(LineOfFile);
  337.  
  338.   if siteList^.Show <> cmCancel then SaveSiteList else
  339.   begin                                      {display file contents in box}
  340.      SiteList^.FreeAll;
  341.      Dispose(SiteList,Done);
  342.      Dispose(ViewDialog,Done);
  343.   end;
  344.   end {if CurrentFile <> ''}
  345.   else
  346.     begin
  347.     MessageBox ('Can''t find ' + CurrentFile + '.',nil,
  348.                 mfError + mfOkButton);
  349.     CurrentFile := '';
  350.     end;
  351.   end; {end if not cmCancel}
  352. Dispose (D,Done);
  353. end;
  354.  
  355. { In this demo, SavesiteList is called only by NewsiteList or
  356.   OpensiteList when it's time to save the currently active Tsitecoll
  357.   object. If CurrentFile is null, SavesiteList opens a conventional file
  358.   dialog; otherwise, the Tsitecoll is saved to CurrentFile.               }
  359.  
  360. procedure SaveRecords(SiteList: PSiteColl);
  361. var F: text;
  362.  
  363.   {note that this proc SaveAction is a FAR, LOCAL procedure; it is
  364.    contained within SaveRecords, so it is local, yet must be declared FAR}
  365.  
  366. procedure SaveAction ( P : PSite ) ; FAR ;
  367. begin
  368.   with P^ do
  369.   begin
  370.     writeln (F, Name,'/', Latitude, '/',Longitude ) ;
  371.     writeln (F, Info )
  372.   end;
  373. end ;
  374.  
  375. begin
  376.   Assign (F,SiteApp.CurrentFile);
  377.   {$I-} Rewrite (F); {$I+}
  378.   if IOResult <> 0 then
  379.                    MessageBox( #3'Error saving to new file'#13+
  380.                                #3+SiteApp.CurrentFile,nil,mfError+mfOkButton);
  381.   SiteList^.ForEach ( @SaveAction ) ;
  382.   Close(F);
  383. end;
  384.  
  385. procedure TsiteApp.SavesiteList;
  386. var
  387.   D: PFileDialog;
  388.   F: text;
  389.   i: integer;
  390. begin
  391.   if CurrentFile = '' then
  392.   begin
  393.     D := New (PFileDialog,Init ('*.DAT','Save site file','~N~ame',
  394.                                 fdOKButton + fdHelpButton,100));
  395.     if Desktop^.ExecView (D) <> cmCancel then D^.GetFileName (CurrentFile);
  396.     Dispose (D,Done);
  397.   end;
  398.   SaveRecords(SiteList);
  399.   SiteList^.FreeAll;
  400.   Dispose(ViewDialog,Done);
  401.   Dispose(SiteList,Done);
  402. end;
  403.  
  404. procedure Detrail(var T: String14);         {trim trailing blanks}
  405. begin
  406.     while T[Length(T)] = ' ' do Dec(T[0])
  407. end;
  408.  
  409.   {------------------------ end of SiteColl methods -----------------------}
  410.  
  411.  
  412.   {------------------------------- TSiteDialog ----------------------------}
  413.  
  414.  { TSiteDialog.Init is pretty much unchanged from your NewDialog
  415.    procedure, except that the field names declared above are used to
  416.    identify the various fields. }
  417.  
  418. constructor TSiteDialog.Init;
  419. var
  420.    R: TRect;
  421. begin
  422.  R.Assign (10,2,65,18);
  423.  TDialog.Init (R,'Flight Details');
  424.  Options:= Options or ofCentered;
  425.  
  426.  R.Assign (3,8,18,9);
  427.  SSiteName[0] := New (PInputLine,Init (R,60));
  428.  Insert (SSiteName[0]);
  429.  R.Assign (2,7,24,8);
  430.  Insert (New (PLabel,Init (R,'Site 1',SSiteName[0])));
  431.  
  432.  R.Assign (22,8,32,9);
  433.  SSiteLat[0] := New (PInputLine,Init (R,13));
  434.  Insert (SSiteLat[0]);
  435.  R.Assign (21,7,31,8);
  436.  Insert (New (PLabel,Init (R,'Latitude',SSiteLat[0])));
  437.  
  438.  R.Assign (34,8,44,9);
  439.  SSiteLong[0] := New (PInputLine,Init (R,13));
  440.  Insert (SSiteLong[0]);
  441.  R.Assign (33,7,43,8);
  442.  Insert (New (PLabel,Init (R,'Longitude',SSiteLong[0])));
  443.  
  444.  R.Assign (3,11,18,12);
  445.  SSiteName[1] := New (PInputLine,Init (R,60));
  446.  Insert (SSiteName[1]);
  447.  R.Assign (2,10,24,11);
  448.  Insert (New (PLabel,Init (R,'Site 2',SSiteName[1])));
  449.  
  450.  R.Assign (22,11,32,12);
  451.  SSiteLat[1] := New (PInputLine,Init (R,13));
  452.  Insert (SSiteLat[1]);
  453.  R.Assign (21,10,31,11);
  454.  Insert (New (PLabel,Init (R,'Latitude',SSiteLat[1])));
  455.  
  456.  R.Assign (34,11,44,12);
  457.  SSiteLong[1] := New (PInputLine,Init (R,13));
  458.  Insert (SSiteLong[1]);
  459.  R.Assign (33,10,43,11);
  460.  Insert (New (PLabel,Init (R,'Longitude',SSiteLong[1])));
  461.  
  462.  R.Assign (3,2,10,4);
  463.  Insert (New (PButton,Init (R,'~O~k',cmOk,bfDefault)));
  464.  R.Assign (12,2,22,4);
  465.  Insert (New (PButton,Init(R,'Cancel',cmCancel,bfNormal)));
  466.  R.Assign (24,2,37,4);
  467.  Insert (New (PButton,Init(R,'Site List',cmSiteList,bfNormal)));
  468. end; {NewDialog}
  469.  
  470.  
  471. { The only event which TSiteInfoDialog.HandleEvent handles specially is the
  472.    cmSiteList event. }
  473.  
  474. procedure TSiteDialog.HandleEvent (var Event: TEvent);
  475. var
  476.    ListDialog: PListDialog;
  477.    SiteNum: Word;
  478.    LSite: PLSite;       
  479. begin                   
  480.  TDialog.HandleEvent (Event);
  481.  if SiteApp.CurrentFile = ''
  482.  then begin EnableCommands([cmOk,cmCancel]);    {don't pop open the list}
  483.             DisableCommands([cmSiteList]);      {unless there is one to }
  484.       end                                       {read from CurrentFile  }
  485.  else EnableCommands([cmOk,cmCancel,cmSiteList]);
  486.                                          
  487.  if Event.What = evCommand then
  488.     begin
  489.     case Event.Command of
  490.     cmSiteList:
  491.     if SiteApp.CurrentFile <> '' then
  492.     begin
  493.  
  494.  { Create a new list dialog box. }
  495.  
  496.    ListDialog := New (PListDialog,Init);
  497.  
  498.  { ExecView it and check the value returned. If the list dialog box was
  499.    closed by the user pressing the OK button, we have more work to do. }
  500.  
  501.    if Desktop^.ExecView (ListDialog) = cmOK then
  502.      begin
  503.  
  504.  { Note that, although the list dialog box has finished executing at this
  505.    point, and is no longer visible on the screen, we haven't disposed of
  506.    it yet, so we can still get the information out of it, such as which
  507.    item in the list box is selected, etc. }
  508.  
  509.  { The radio button cluster (SiteType) holds the information telling us which
  510.    site we'll be assigning values to. }
  511.  
  512.      SiteNum := ListDialog^.SiteType^.Value;
  513.  
  514.      with ListDialog^.SitePicklist^ do
  515.        begin
  516.  
  517.  { Get a pointer to the selected site in the list box. }
  518.  
  519.        LSite := PLSite (List^.At (Focused));
  520.  
  521.  { Copy the data from the TLSite in the list box to the appropriate input
  522.    lines. }
  523.  
  524.        SSiteName[SiteNum]^.Data^ := LSite^.LName^;
  525.        SSiteLat[SiteNum]^.Data^  := LSite^.LLat^;
  526.        SSiteLong[SiteNum]^.Data^ := LSite^.LLong^;
  527.        end;
  528.  
  529.  { Refresh the screen display with the new values. }
  530.  
  531.      SSiteName[SiteNum]^.DrawView;
  532.      SSiteLat[SiteNum]^.DrawView;
  533.      SSiteLong[SiteNum]^.DrawView;
  534.      end;
  535.  
  536.  { We're done with the list dialog box at this point, so we can get rid of it. }
  537.  
  538.      if TheLSiteCollection <> nil then
  539.         begin
  540.         TheLSiteCollection^.FreeAll;
  541.         end;                                   
  542.      if ListDialog <> nil then Dispose (ListDialog,Done);
  543.    end; {end of cmSiteDialog}
  544.    end; {end case}
  545.    end;
  546.    ClearEvent(Event);
  547. end;
  548.   {------------------------ end of TSiteDialog ----------------------------}
  549.  
  550.   {------------------------ ListDialog & Listbox --------------------------}
  551.  
  552. function CreateList: PLSiteCollection; {was string}
  553. var
  554.   F: Text;
  555.   S: String;
  556.   List: PLSiteCollection;
  557.   FileSite: TSiteRec;
  558.   LineOfFile: Pword;
  559. begin
  560.   List := New(PLSiteCollection, Init(50, 25));
  561.   List^.Duplicates:= true;
  562.   Assign(F, SiteApp.CurrentFile);
  563.   {$I-} Reset(F); {$I+}
  564.   if IOResult <> 0 then
  565.      MessageBox( #3'Error reading from site file'#13+
  566.                  #3+SiteApp.CurrentFile,nil,mfError+mfOkButton);
  567.   New(LineOfFile);
  568.   LineOfFile^ := 0;
  569.   while not Eof(F) do
  570.   begin
  571.     Readln(F, S);
  572.     Inc(LineOfFile^);
  573.     if length(S) > 0 then
  574.       begin   
  575.        with FileSite {SiteData[LineOfFile^]} do
  576.         begin
  577.          FNum  := LineOfFile^;
  578.          FName := copy(S, 1, (pos('/', S) - 1)); Delete(S, 1, pos('/', S));
  579.          FLat  := copy(S, 1, (pos('/', S) - 1)); Delete(S, 1, pos('/', S));
  580.          Detrail(FLat);
  581.          FLong := S;
  582.          Detrail(FLong);
  583.          Readln(F,S);
  584.          FInfo := S;
  585.          List^.Insert(New(PLSite,Init (FName,FLat,FLong,FInfo)))
  586.         end
  587.       end
  588.   end;
  589.   Close(F);
  590.   Dispose(LineOfFile);
  591.   CreateList := List;
  592. end; {function CreateList}
  593.  
  594. { TSiteListBox.GetText knows that the list box contains items of type TSite,
  595.   so it extracts the Name field from the item. }
  596.  
  597.  function TLSiteListBox.GetText (Item: Integer; MaxLen: Integer): String;
  598.  
  599.  begin
  600.  GetText := Copy (PLSite (List^.At (Item))^.LName^,1,MaxLen);
  601.  end;
  602.  
  603.  
  604. { Draw the dialog, listbox, and buttons for the list dialog box.      }
  605. { TListDialog.Init is pretty much the same as your ListDlg procedure. }
  606.  
  607. constructor TListDialog.Init;
  608. var
  609.    R: TRect;
  610.    Scroll: PScrollBar;
  611. begin
  612.  R.Assign (10,1,60,22);
  613.  TDialog.Init (R,'List');
  614.  R.Assign (47,2,48,10);
  615.  Scroll := New (PScrollBar,Init (R));
  616.  Insert (Scroll);
  617.  R.Assign (2,2,47,10);
  618.  SitePicklist := New (PLSiteListBox,Init (R,1,Scroll));
  619.  Insert (SitePicklist);
  620.  R.Assign (1,1,46,2);
  621.  Insert (New (PLabel,Init(R,'~S~ites in file ' + SiteApp.CurrentFile,SitePicklist)));
  622.  R.Assign (5,13,31,17);
  623.  SiteType := New (PRadioButtons,Init (R,
  624.    NewSItem ('Start point',
  625.    NewSItem ('Finish point',
  626.    nil))));
  627.  Insert (SiteType);
  628.  R.Assign (4,12,31,13);
  629.  Insert (New (PLabel,Init (R,'~D~estination for site data:',SiteType)));
  630.  R.Assign (4,18,16,20);
  631.  Insert (New (PButton,Init (R,'Cancel',cmCancel,bfNormal)));
  632.  R.Assign (24,18,36,20);
  633.  Insert (New (PButton,Init (R,'~O~K',cmOK,bfDefault)));
  634.  
  635.  { This is the way to attach a new collection to the list box. Note that
  636.    the disk file will be re-read every time the list dialog box is opened.
  637.    Whether this is desirable or not depends on the application. Another
  638.    technique would be to call CreateList only once, at the start of the
  639.    application, and store its results in a global variable (call it TheList).
  640.    Then you'd just do a SitePicklist^.NewList (TheList) here. }
  641.  
  642.  SitePicklist^.NewList (CreateList);
  643. end;
  644.  
  645.  { TSiteListBox.HandleEvent responds to a double-click on an item in the
  646.    list box by (1) selecting that item, and (2) shoving a cmOK command into
  647.    the event queue, to simulate the user pressing the OK button. That way,
  648.    double-clicking ends up having the same effect as single-clicking the item
  649.    followed by pressing the OK button. }
  650.  
  651.  procedure TLSiteListBox.HandleEvent (var Event: TEvent);
  652.  begin
  653.  if Event.What = evMouseDown then if Event.Double then
  654.    begin
  655.    Event.What := evCommand;
  656.    Event.Command := cmOK;
  657.    Event.InfoPtr := @Self;
  658.    PutEvent (Event);
  659.    ClearEvent (Event);
  660.    end;
  661.  TListbox.HandleEvent(Event);
  662.  end;
  663.   {--------------------- end of ListDialog & Listbox ---------------------}
  664.  
  665.   {--------------------- Main application procedures ---------------------}
  666.   { This is the key to the pick and copy.  Note that the the input dialog
  667.     is created from the Application's handleEvent, and the List dialog is
  668.     created by the input dialog's handleEvent, and the listbox is created
  669.     by the list dialog's handleEvent.  As each box closes, it returns
  670.     control automatically to the previous handleEvent.  This saves a more
  671.     complicated arrangement of broadcast messages (which I tried but was
  672.     unable to get working properly: control always ended up in the wrong
  673.     place after all the dialog boxes were closed -- SM       }
  674.  
  675. procedure TsiteApp.HandleEvent (var Event: TEvent);
  676. var
  677.   C: word;
  678.   SiteDialog: PSiteDialog;
  679.   S: string;
  680. begin
  681. TApplication.HandleEvent (Event);
  682. if Event.What = evCommand then
  683.   begin
  684.   case Event.Command of
  685.     cmNew: NewsiteList;
  686.     cmOpen: OpensiteList;
  687.     cmNewDialog:
  688.      begin
  689.      SiteDialog := New (PSiteDialog, Init);
  690.      SiteDialog^.SetData(SiteDialogData);
  691.      C:=Desktop^.Execview(SiteDialog); 
  692.      if C  <> cmCancel then SiteDialog^.GetData(SiteDialogData);
  693.  
  694.      {your processing of the input data would go here}
  695.  
  696.      if SiteDialog <> nil then Dispose(SiteDialog,done);
  697.      end;
  698.     else Exit;
  699.     end;
  700.   ClearEvent(Event);
  701.   end;
  702. end;
  703.  
  704. procedure TsiteApp.InitMenuBar;
  705. var
  706.   R: TRect;
  707. begin
  708. GetExtent (R);
  709. R.B.Y := R.A.Y + 1;
  710. MenuBar := New (PMenuBar,Init (R,NewMenu (
  711.   NewSubMenu ('~F~ile',hcNoContext,NewMenu (
  712.     NewItem ('~N~ew Site list','F3',kbF3,cmNew,hcNoContext,
  713.     NewItem ('~O~pen Site file...','F5',kbF5,cmOpen,hcNoContext,
  714.     NewLine (
  715.     NewItem ('~N~ew Dialog','',kbNoKey,cmNewDialog,hcNoContext,
  716.     NewLine (
  717.     NewItem ('E~x~it','Alt-X',kbAltX,cmQuit,hcNoContext,nil))))))),nil))));
  718. end;
  719.  
  720. procedure TsiteApp.InitStatusLine;
  721. var
  722.   R: TRect;
  723. begin
  724. GetExtent (R);
  725. R.A.Y := R.B.Y - 1;
  726. StatusLine := New (PStatusLine,Init (R,
  727.   NewStatusDef (0,$FFFF,
  728.     NewStatusKey ('~F3~ New',kbF3,cmNew,
  729.     NewStatusKey ('~F5~ Open',kbF5,cmOpen,
  730.     NewStatusKey ('~Alt-X~ Exit',kbAltX,cmQuit,
  731.     NewStatusKey ('',kbF10,cmMenu,nil)))),nil)));
  732. end;
  733.  
  734. destructor TSiteApp.Done;   {■■■■■■■■■■■■■■■■■■■■■■■■■}
  735. var
  736.   Event: TEvent;
  737. begin                    {de-allocate memory in reverse order to allocation}
  738.   Dispose(HeapViewer,Done);
  739.   TApplication.Done;
  740. end;                     {I was unable to clear all the heap memory;}
  741.                          {if you can figure it out, let me know! -SM}
  742.  
  743. begin
  744.   siteApp.Init;
  745.   siteApp.Run;
  746.   siteApp.Done;
  747. end.
  748.  
  749.